package swardToOffer.method_2_sort_or_find.find;

import java.util.PriorityQueue;

/**
 * 求最长回文子串
 回文子串的特征：A*BA*型
 方法一：动态规划
 对于一个子串而言，如果它是回文串，并且长度大于 2，
 那么将它首尾的两个字母去除之后，它仍然是个回文串。例如对于字符串 “ababa”，
 如果我们已经知道 “bab” 是回文串，那么 “ababa” 一定是回文串，这是因为它的首尾两个字母都是 “a”。
 根据这样的思路，我们就可以用动态规划的方法解决本题。
 我们用 P(i,j)表示字符串s的第 i 到 j 个字母组成的串（下文表示成 s[i:j]）是否为回文串：
 那么我们就可以写出动态规划的状态转移方程：
 P(i,j)=P(i+1,j−1)^(s[i]==s[j])
 即只有 s[i+1:j-1]是回文串，并且 s 的第 i 和 j 个字母相同时，s[i:j]才会是回文串。
 递归边界：即子串的长度为 1 或 2。对于长度为 1 的子串，它显然是个回文串；
 对于长度为 2 的子串，只要它的两个字母相同，它就是一个回文串。因此我们就可以写出动态规划的边界条件：
    P(i,i) = true;
    P(i,i+1) = (s[i]==s[i+1])
 方法二：中心扩展算法
 枚举所有的「回文中心」并尝试「扩展」，直到无法扩展为止，
 此时的回文串长度即为此「回文中心」下的最长回文串长度。我们对所有的长度求出最大值，即可得到最终的答案。
 */
public class LongestPalindromeSubstring {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) {
            return "";
        }
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);//len为奇数如s=[aba]
            int len2 = expandAroundCenter(s, i, i + 1); //len为偶数如s=[abba]
            int len = Math.max(len1, len2);
            if (len > end - start) {
                //abba 此时i=1,len=4,求出start=0=i-(len-1)/2,end=3=i+len/2,
                //aba,此时i=1,len=3,start=0=i-(len-1)/2,end=i+len/2;
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    /**
     * 基于回文中心进行扩展
     * @param s 原始字符串
     * @param left 扩展的左起点
     * @param right 扩展的右起点
     * @return 以中心(left+right)/2进行扩展的最长回文子串长度
     */
    public int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }
        return right - left - 1;
    }


    //dp[i][j] = dp[i+1][j-1] if dp[i-1][j+1] && s[i]==s[j]
    //边界问题，dp[i][i]=true,dp[i][i+1] = true if dp[i]==dp[i+1]
    public String longestPalindrome1(String s) {
        char[] arr = s.toCharArray();
        int len = arr.length;
        if (len<2) return s;
        //初始化dp
        boolean[][] dp = new boolean[len][len];
        for(int i=0;i<len;i++){
            dp[i][i]=true;
        }
        int maxLen = 1;
        int begin = 0;
        //自底向上进行推导
        for(int length=2;length<=len;length++){
            //i为回文子串的左边界,j为回文子串的右边界
            for(int i=0; i<len;i++){
                int j=i+length-1;
                if(j>=len) break;
                if(arr[i]!=arr[j]){
                    dp[i][j]=false;
                }else{
                    //dp[i][i+1] = true if dp[i]==dp[i+1]
                    if(length==2){
                        dp[i][j]=true;
                    }else{
                        dp[i][j]=dp[i+1][j-1];
                    }
                }
                if(dp[i][j] && length>maxLen){
                    maxLen = length;
                    begin = i;
                }

            }
        }
        return s.substring(begin,begin+maxLen);
    }


    public static void main(String[] args) {
        LongestPalindromeSubstring Main = new LongestPalindromeSubstring();
        System.out.println(Main.longestPalindrome("aacabdkacaa"));
    }
}
